import json
import re
from django import http
from django.contrib.auth import login
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
from django_redis import get_redis_connection

from apps.users.models import User,Address


# 1.先确定路由和提交方式  POST   register/
# 2。据需求把大体步骤写下来，千万别信前端给的数据！！！！！！！
#     1.接收数据
#     2.验证数据
#     3.保存数据

# 3.具体实心思路
#     1.接收数据 request。POST
#     2.分别获取数据 username.password
#     3.判断要求的数据是否齐全，
#     4.验证用户名
#     5.验证密码
#     6.验证确认密码
#     7验证手机号
#     8验证是否同意协议
#     9保存数据
#     10 跳转首页
import logging

from apps.users.utils import generic_verify_email_url, check_veryfy_email_token
from meiduo import settings
from utils.Views import LoginRequiredJSONMixin
from utils.response_code import RETCODE

logger = logging.getLogger('django')
# 注册
class RegisterView(View):
    def get(self,request):

        return render(request,'register.html',)

    def post(self, request, ):
        # 接受数据
        data = request.POST
        # 分别获取 账户密码
        username=data.get('username')
        password=data.get('password')
        password2=data.get('password2')
        mobile=data.get('mobile')
        allow=data.get('allow')

        sms_code_client = data.get('sms_code')

        if not all([username,password,password2,mobile,allow,sms_code_client]):

            return http.HttpResponseBadRequest('参数不全')

        # if not re.match(r'^[0-9a-zA-Z_-]{5,20}$',username):
        #
        #     return http.HttpResponseBadRequest('用户名不满足条件')
        if not re.match(r'^[0-9a-zA-Z_-]{5,20}$',username):
            return http.HttpResponseBadRequest('用户名不满足条件')

        if not re.match(r'^[0-9A-Za-z]{8,20}$',password):
            return http.HttpResponseBadRequest('密码格式不正确')

        if password2!=password:
            return http.HttpResponseBadRequest('密码不一致')

        if not re.match(r'^1[3-9]\d{9}$',mobile):
            return http.HttpResponseBadRequest('手机号不符合规则')

        if allow != 'on':
            return http.HttpResponseBadRequest('您未同意协议')
        # 验证用户提交的短信验证码和redis的短信验证码是否合适
        # 连接redis
        redis_conn = get_redis_connection('code')

        # 获取redis中的短信验证码
        sms_code_server = redis_conn.get('sms_%s' %mobile)

        # 判断redis是否过期
        if not sms_code_server:
            return http.HttpResponseBadRequest('短信验证码过期')
        # 比对
        if sms_code_server.decode() !=sms_code_client:
            return http.HttpResponseForbidden('短信验证码不一致')
        try:
            # create_user 是系统提供的 自动对密码进行加密的方法,同时也会会创建用户
            user = User.objects.create_user(
                username=username,
                password=password,
                mobile=mobile
            )
        except Exception as e:
            logger.error(e)
            # print(e)
            return render(request, 'register.html', context={'register_errmsg': '创建用户失败'})
        # 保持登陆状态，设置seesion
        from django.contrib.auth import login
        login(request,user)

            # 跳转首页,
        return redirect(reverse('contents:index'))

        # return http.HttpResponse('ok')


    # 1,需求
    #     当用户输入子输入用户名后 ，光标失去焦点之后 前段发送一个ajax请求
    #     这个ajax请求 需要包含一个参数 username
    # 2.后台
    #     1，确定请求方式和路由
    #             提取URL的他额定部分，如/weather/beijing/2018可以在服务器端的路由中用正则表达式截取；
    #             查询字符串（query string)，形如key1 = value1 & key2 = value2
    #
    #             GET路由
    #             usernames / (?P < username > [a - zA - Z0 - 9_]{5, 20}) /
    #             register / count /?username = username
        #     2.大体步骤写下来
        #         一。参数校验
        #         二。根据用户名进行查询
        #         三。返回响应

#用户名是否重复
class RegisterUsernameCountView(View):

    def get(self,request,username):
        count = User.objects.filter(username=username).count()

        return http.JsonResponse({'count':count})


# 登陆/
class LoginView(View):
    """
    需求，
        当用户将用户名和密码编写完成之后，前段需要将用户名和密码发送给后端
    后台
        请求方式和路由         post  login


    """
    def get(self,request):
        return render(request,'login.html')

    def post(self,request):
        # 大体步骤
        # 1接受数据
        data = request.POST
        # 2获取数据
        username=data.get('username')
        password=data.get('password')
        remembered=data.get('remembered')
        # 3验证是否齐全（用户名和密码都要传递过来）
        if not all([username,password]):
            return http.HttpResponseBadRequest('参数不全')
        # 4验证用户名
        if not re.match(r'^[0-9a-zA-Z_-]{5,20}$', username):
            return http.HttpResponseBadRequest('用户名不满足条件')
        # 5判断密码是否符合规则
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseBadRequest('密码格式不正确')
        # 6用户名和密码是否正确
        # ①可以根据用户名查询，在判断密码
        # ②采用系统自带的认证
        from django.contrib.auth import authenticate
        # authenticate(username=xx,password=xx)
        # 如果我们传递的用户名和密码正确，他会返回user信息
        # 不正确，则不返回
        user=authenticate(username=username,password=password)
        if user is None:
            return render(request,'login.html',{'login_error_message':'用户名或密码错误'})

        # 7保持会话
        login(request,user)
        # 记住登陆或者不记录登陆
        if remembered != 'on':
            # 簿记录登陆状态
            request.session.set_expiry(0)

        else:
            request.session.set_expiry(None)

            # is_authenticated 判断用户是否登陆
            # 如果登陆返回true、
            # 如果没有登陆返回false
    # 先获取next参数
        next = request.GET.get('next')
        if next:
            response = redirect(next)
        else:

            # 8返回响应  设置cookie信息
            response =  redirect(reverse('contents:index'))

        # response.set_cookie(key,value,max_age=)
        if remembered != 'on':
            # 簿记录登陆状态
            response.set_cookie('username', username, max_age=None)

        else:
            response.set_cookie('username',username,max_age=14*24*3600)

        return response


# 退出登陆
from django.contrib.auth import logout
class LogoutView(View):

    def get(self,request):
        # 1.调用系统的推出方法（系统的退出方法原理，删除session信息）
        # 2.吧cookie信息（username）清除一下，因为我们的首页是以username信息确认是否登陆的
        logout(request)
        response = redirect(reverse('contents:index'))

        # 清除cookie数据
        response.delete_cookie('username')

        return response


# django自带的认证方法，判断用户是否登陆
from django.contrib.auth.mixins import LoginRequiredMixin


class UserInfoView(LoginRequiredMixin,View):
    # 必须是登陆的用户才可以访问
    # 如果没有登陆，默认会跳闸un到系统的登陆路由
    # 系统的登陆路由是  accounts/login/
    def get(self,request):
        # 1.获取指定的数据 组织上下文
        context ={
            'username':request.user.username,
            'mobile': request.user.mobile,
            'email':request.user.email,
            'email_active':request.user.email_active
        }

        # 2.

        return render(request,'user_center_info.html',context=context)


    """
    需求（用户做了说明，后台需要做什么，【需要让前段提供什么，后端接受什么）
    当用户在邮箱输入框中输入邮箱地址之后，点击保存需要让前段将邮箱信息及用户信息发送给后端
    后端：
        确定求求方式和路由： put  /emails/
        大体步骤：
            1.必须是登陆用户才可以更新邮箱信息
            2.接受用户提交的邮箱信息
            3.验证邮箱信息是否机复合由相规则
            4.保存是数据
            5.发送激活邮件，
            6返回响应

            GET :获取数据
            POST    ：新增数据
            PUT     ：更新数据/修改数据 put请求和post类似 请求数据在body内
            DELETE  ：删除
    """
# LoginRequiredMixin会进行一个重定向、
# 我们这是进行的ajax 请求，我们应该返回一个json数据
# 绑定邮箱
class EmailView(LoginRequiredJSONMixin,View):

    def put(self,request):

        # 必须是登陆用户才可以更新邮箱信息
        # if request.user.is_active:


        # 接受用户提交的邮箱信息
        # data = request.POST
        # 1.获取body数据
        body = request.body
        # 2.body数据是bytes类型，进行类型转换
        body_str = body.decode()
        #对字符串json，进行转换
        data = json.loads(body_str)
        # email = data.get('email')

        email = data.get('email')

        # 验证邮箱信息是否机复合由相规则
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$',email):
            return http.JsonResponse({'code':RETCODE.PARAMERR,'errmsg':'参数错误'})

        # 更新数据
        try:
            request.user.email=email
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':RETCODE.DBERR,'errmsg':'更新错误'})

        # 发送激活邮件
        from django.core.mail import send_mail

        # subject, message, from_email, recipient_list,
        # subject,               主题
        subject = '梅朵商城激活邮件'

        # message,              消息
        # message = 'message'
        message=''
        # html_message = "<a href='#'>戳我,戳我,戳我有惊喜</a>"
        # 激活的url中包含用户的信息就可以
        verify_url = generic_verify_email_url(request.user.id)

        html_message = '<p>尊敬的用户您好！</p>' \
                   '<p>感谢您使用美多商城。</p>' \
                   '<p>您的邮箱为：%s 。请点击此链接激活您的邮箱：</p>' \
                   '<p><a href="%s">%s<a></p>' % (email, verify_url, verify_url)

        # from_email,           谁发的邮件
        from_email = settings.EMAIL_FROM
        # recipient_list,       收件人列表 []
        recipient_list = [email]
        # send_mail(
        #     subject=subject,
        #     message=html_message,
        #     from_email=from_email,
        #     recipient_list=recipient_list,
        #     html_message=html_message
        # )
        # from celery_tasks.email.tasks import send_verify_email
        from celery_tasks.email.tasks import send_verify_email
        send_verify_email.delay(
            subject=subject,
            message=message,
            from_email=from_email,
            recipient_list=recipient_list,
            html_message=html_message)

        # 返回响应
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok'})


    """
    需求：
        当用户点击激活连接的时候，可以展示该页面，同事获取前段提交过来的token
    后端：
    请求方式 和路由  GET emails/verification
        # 1.接受token
        # 2.验证token
        # 3.根据user——id查询用户信息
        # 4.改变用户信息
        # 5.返回响应
    """


# 邮箱激活验证
class EmailVerifyView(View):
    def get(self,request):
        # 1.接受token
        token = request.GET.get('token')
        if token is None:
            return http.HttpResponseBadRequest('参数错误')
        # 2.验证token

        user_id = check_veryfy_email_token(token)
        if user_id is None:
            return http.HttpResponseBadRequest('参数有误')
        # 3.根据user——id查询用户信息
        try:
            # pk primary key 主键的意思
            # 如果我们不记得主键是哪个字段的时候,可以直接使用pk
            # 系统会自动使用主键
            # user = User.objects.get(pk=user_id)
            user = User.objects.get(id=user_id)
            # 4.改变用户信息
            if user is not None:
                user.email_active=True
                user.save()
        except User.DoesNotExist:
            return http.HttpResponseBadRequest('canshuyouwu')

        # 5.返回响应
        return redirect(reverse('users:info'))

    """
    需求:
        当用户点击激活连接的时候,可以展示该页面,同时,获取前端提交过来的token
    后端:

        请求方式和路由:
            GET     emails/verification

        1.接收token
        2.验证token
        3.根据user_id查询用户信息
        4.改变用户信息
        5.返回相应(跳转到个人中心页面)
    """
# 收货地址栏
class AddressView(LoginRequiredMixin,View):

    def get(self,request):

        # 1.必须得是登陆用户
        login_user = request.user
        print(type(login_user))

        # 2.查询登陆用户的地址信息

        addresses = Address.objects.filter(user=request.user,is_deleted=False)
        # 3.对列表数据进行转换，转成字典列表
        addresses_list =[]
        for address in addresses:
            addresses_list.append({
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "province_id": address.province_id,
                "city": address.city.name,
                "city_id": address.city_id,
                "district": address.district.name,
                "district_id": address.district_id,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            })
        # 4.传递给模板
        context = {
            "addresses":addresses_list,
            'default_address_id':login_user.default_address_id
        }

        return render(request,'user_center_site.html',context=context)



# 新增收货地址
class CreateAddressView(LoginRequiredMixin,View):

    """
    需求:
        当用户填写完新增数据之后,点击新增按钮,需要让前端将 收货人等信息提交给后端

    后端:

        大体步骤:
        # 1.判断当前用户是否登陆
        # 2.接收参数
        # 3.验证参数
        # 4.数据入库
        # 5.返回相应

        请求方式和路由:
            POST    /addresses/create/
    """
    def post(self,request):

        # 判断用户的地址数量是否超过20
        count = Address.objects.filter(user=request.user,is_deleted=False).count()
        if count >20:
            return http.JsonResponse({'code':RETCODE.THROTTLINGERR,'errmsg':"个数超过伤心啊"})


        # 1.判断当前用户是否登陆
        # if request.user.is_authenticated

        # 2.接收参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver') #收货人
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')
        # 3.验证参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseBadRequest('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseBadRequest('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseBadRequest('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseBadRequest('参数email有误')


        # 4.数据入库
        try:
            address = Address.objects.create(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
            # 如果没有默认地址，就设置新地址为默认地址
            if not request.user.default_address:
                request.user.default_address=address
                request.user.save()


        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':RETCODE.DBERR,'errmsg':'数据库操作失败'})

        # 5.返回相应
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'ok','address':address_dict})


#修改收货地址
class UpdateAddressView(LoginRequiredMixin,View):
    """
       需求: 当用户修改了地址信息之后,需要让前端将 这个信息全都收集过去
       后端:

           # 1.判断用户是否登陆
           # 2.根据传递过来的更新指定的地址信息
           # 3.更新
           # 4.返回相应

       请求方式和路由
           GET addresses/id/
           POST addresses/id/
           PUT addresses/id/
       """

    def put(self,request,address_id):
        # 1.判断用户是否登陆
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 2.根据传递过来的更新指定的地址信息
        # address = Address.get(pk=address_id)
        # address.receiver = data.get('recever')
        # 3.更新
        # 更新成功之后,返回 1 表示更新成功
        # 返回 0 表示更新失败
        try:
            Address.objects.filter(pk=address_id).update(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
        )
            # 再次查询一下地址信息
            address = Address.objects.get(pk=address_id)
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':RETCODE.DBERR})
        # 4.返回相应
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        return http.JsonResponse({'address':address_dict,'code':RETCODE.OK})
        pass

    def delete(self,request,address_id):

        try:
            address = Address.objects.get(pk=address_id)
            address.is_deleted=True
            address.save()
        except Exception as e:
            pass

        return http.JsonResponse({'code':RETCODE.OK})



"""
增
    1.接收数据
    2.验证数据
    3.数据入库
    4.返回相应
删
    1.根据id进行查询
    2.删除就行
改
    1.先接收要修改哪个数据(根据id进行查询)
    2.接收修改之后的数据
    3.验证数据
    4.更新数据(保存数据)
    5.返回相应
查
    1.根据已知条件进行查询
    2.查询出来的是对象列表,我们需要将对象列表转换为字典
    3.返回数据

"""
